//
//  CoGeMoviePlayerPlugIn.m
//  CoGeMoviePlayer
//
//  Created by Tamas Nagy on 1/21/12.
//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//

/* It's highly recommended to use CGL macros instead of changing the current context for plug-ins that perform OpenGL rendering */
#import <OpenGL/CGLMacro.h>
#import <mach/task_info.h>

#import "CoGeMoviePlayerPlugIn.h"

#define	kQCPlugIn_Name				@"CoGeMoviePlayer"
#define	kQCPlugIn_Description		@"CoGeMoviePlayer description"


static void _TextureReleaseCallback(CGLContextObj cgl_ctx, GLuint name, void* info)
{
	//glDeleteTextures(1, &name);
}



@implementation CoGeMoviePlayerPlugIn

/*
 Here you need to declare the input / output properties as dynamic as Quartz Composer will handle their implementation
 @dynamic inputFoo, outputBar;
 */

@dynamic inputCoGeLayerFilePathes, inputMovieIndex, inputSlotClicked, inputIsActive, inputRate, inputVolume, inputStartPoint, inputEndPoint, inputPlayhead, inputBankChanged, inputLoopMode, inputAudioDeviceUID, inputNoResetOnTrigger, inputPlayHeadSetByMouse, inputPreloadMovies;
@dynamic outputMovieReachedEndPoint,outputImage, outputMovieTime, outputSlotMarkedToTrigger, outputMoviePlayerInited, outputPreloadStateChanges;

@synthesize movieData;
@synthesize workingStartPoint;
@synthesize workingEndPoint;
@synthesize movieSize;
@synthesize movieOKToPlayBack;
@synthesize workingRate;
@synthesize workingVolume;
@synthesize movieChanged;
@synthesize workingLoopMode;
@synthesize moviePathes;
@synthesize shouldStartMovie;
@synthesize readyToSetPlayhead;
@synthesize isPlaybackHAPMovie;
@synthesize nextSlotToTrigger;
@synthesize isUsingPreloadedMedia;


+ (NSDictionary*) attributes
{
	/*
	 Return a dictionary of attributes describing the plug-in (QCPlugInAttributeNameKey, QCPlugInAttributeDescriptionKey...).
	 */
	
	return [NSDictionary dictionaryWithObjectsAndKeys:kQCPlugIn_Name, QCPlugInAttributeNameKey, kQCPlugIn_Description, QCPlugInAttributeDescriptionKey, nil];
}

+ (NSDictionary*) attributesForPropertyPortWithKey:(NSString*)key
{
	/*
	 Specify the optional attributes for property based ports (QCPortAttributeNameKey, QCPortAttributeDefaultValueKey...).
	 */
	
	if([key isEqualToString:@"outputSlotMarkedToTrigger"])
	{
		return [NSDictionary dictionaryWithObjectsAndKeys:QCPortTypeNumber, QCPortAttributeTypeKey,
				[NSNumber numberWithInt:-1], QCPortAttributeDefaultValueKey,
				@"outputSlotMarkedToTrigger", QCPortAttributeNameKey, nil];
	}
	
	if([key isEqualToString:@"outputMoviePlayerInited"])
	{
		return [NSDictionary dictionaryWithObjectsAndKeys:QCPortTypeBoolean, QCPortAttributeTypeKey,
				[NSNumber numberWithBool:NO], QCPortAttributeDefaultValueKey,
				@"outputMoviePlayerInited", QCPortAttributeNameKey, nil];
	}
	
	return nil;
}

+ (QCPlugInExecutionMode) executionMode
{
	/*
	 Return the execution mode of the plug-in: kQCPlugInExecutionModeProvider, kQCPlugInExecutionModeProcessor, or kQCPlugInExecutionModeConsumer.
	 */
	
	return kQCPlugInExecutionModeProvider;
}

+ (QCPlugInTimeMode) timeMode
{
	/*
	 Return the time dependency mode of the plug-in: kQCPlugInTimeModeNone, kQCPlugInTimeModeIdle or kQCPlugInTimeModeTimeBase.
	 */
	
	return kQCPlugInTimeModeIdle;
}

- (id) init
{
	if(self = [super init]) {
		/*
		 Allocate any permanent resource required by the plug-in.
		 */
				
	}
	
	return self;
}

- (void) finalize
{
	/*
	 Release any non garbage collected resources created in -init.
	 */
	
	[super finalize];
}

- (void) dealloc
{
	/*
	 Release any resources created in -init.
	 */
	
	[super dealloc];
}


@end

@implementation CoGeMoviePlayerPlugIn (Execution)


- (BOOL) startExecution:(id<QCPlugInContext>)context
{
	/*
	 Called by Quartz Composer when rendering of the composition starts: perform any required setup for the plug-in.
	 Return NO in case of fatal failure (this will prevent rendering of the composition to start).
	 */
	//	NSLog(@"startExecution");
    
    [self setNextSlotToTrigger:-1];
	
	self.moviePathes = nil;
    
    [self setIsPlaybackHAPMovie:NO];
    
    skipNextFrame = NO;
    
    currentmovie = nil;
	
    [self setShouldStartMovie:NO];
    [self setReadyToSetPlayhead:YES];
	
	shouldProcessBecauseMediaBankChanged = NO;
    
    if (_latestTextureFrame != NULL) {
        
        CVBufferRelease(_latestTextureFrame);
        
    }

    _latestTextureFrame = NULL;


	/*
	[[NSNotificationCenter defaultCenter] addObserver:self
											 selector:@selector(movieLoadStateDidChange:)
												 name:QTMovieLoadStateDidChangeNotification
											   object:nil];
	
	*/
	
    preloadData = [[CoGeMovieThreadSafeMutableDictionary alloc] init];
    
    preloadQueue = [[NSOperationQueue alloc] init];
    [preloadQueue setMaxConcurrentOperationCount:1];

    preloadstatechanged = [[CoGeMovieThreadSafeMutableDictionary alloc] init];
    
	currentplayback = 0;
    currentbank = -1; //-1 will force resend the state of preloading data
	
	[self setWorkingLoopMode:0];
	
	self.movieData = nil;
	
	NSMutableDictionary *tempd = [[NSMutableDictionary alloc] initWithCapacity:0];
	
	self.movieData = tempd;
	
	[tempd release];
	
	needstrigger = NO;
	
	/*
	 NSDictionary* pixel_buffer_attributes = [NSDictionary dictionaryWithObjectsAndKeys:
	 
	 [NSNumber numberWithInteger:k32BGRAPixelFormat], kCVPixelBufferPixelFormatTypeKey,
	 
	 // Core Video can manage this automagically for us
	 // and actually we don' deal with decode resolution
	 //	 [NSNumber numberWithDouble:self.inputDecodeWidth], kCVPixelBufferWidthKey,
	 //	 [NSNumber numberWithDouble:floorf(self.inputDecodeWidth/originalaspect)], kCVPixelBufferHeightKey,
	 //[NSNumber numberWithInteger:1], kCVPixelBufferBytesPerRowAlignmentKey,
	 [NSNumber numberWithBool:YES], kCVPixelBufferOpenGLCompatibilityKey,
	 
	 nil
	 ];
	 NSDictionary* visual_context_options = [NSDictionary dictionaryWithObjectsAndKeys: pixel_buffer_attributes,
	 kQTVisualContextPixelBufferAttributesKey, 
	 nil];
    
	 */
    
    [self setIsUsingPreloadedMedia:NO];
    
    _context = CGLRetainContext([context CGLContextObj]);
    
    [self initVisualContexts];
    
    AudioDeviceID theDefault;
    UInt32 theSize = sizeof(AudioDeviceID);
    AudioObjectPropertyAddress theAddress = { kAudioHardwarePropertyDefaultOutputDevice,
        kAudioObjectPropertyScopeGlobal,
        kAudioObjectPropertyElementMaster };
    AudioObjectPropertyAddress      deviceAddress;
    
    UInt32                      propertySize;
    CFStringRef     uidString;
    
    propertySize = sizeof(uidString);
    deviceAddress.mSelector = kAudioDevicePropertyDeviceUID;
    deviceAddress.mScope = kAudioDevicePropertyScopeOutput;
    deviceAddress.mElement = kAudioObjectPropertyElementMaster;

    
    OSStatus theError = AudioObjectGetPropertyData(kAudioObjectSystemObject,
                                                   &theAddress,
                                                   0,
                                                   NULL,
                                                   &theSize,
                                                   &theDefault);
    
    theError = AudioObjectGetPropertyData(theDefault, &deviceAddress, 0, NULL, &propertySize, &uidString);
    
    defAudioDevice = [[NSString alloc] initWithString:(NSString *)uidString];
    
    CFRelease(uidString);
    
    NSLog(@"uid: %@", defAudioDevice);
    
    if (!defAudioDevice) defAudioDevice = @"";
	   
	self.outputMoviePlayerInited = NO;
	
	self.outputSlotMarkedToTrigger = [self nextSlotToTrigger];
	
	return YES;
}

-(void)initVisualContexts {
    
    if (_visualContextHAP) {
        
        QTVisualContextRelease(_visualContextHAP);
        _visualContextHAP = NULL;
    }

    if (_visualContextTexture) {
        
        QTVisualContextRelease(_visualContextTexture);
        _visualContextTexture = NULL;
    }

    CFDictionaryRef pixelBufferOptions = HapQTCreateCVPixelBufferOptionsDictionary();
    
    // QT Visual Context attributes
    NSDictionary *visualContextOptions = [NSDictionary dictionaryWithObject:(NSDictionary *)pixelBufferOptions
                                                                     forKey:(NSString *)kQTVisualContextPixelBufferAttributesKey];
    
    CFRelease(pixelBufferOptions);
    
    OSStatus error = QTPixelBufferContextCreate(kCFAllocatorDefault, (CFDictionaryRef)visualContextOptions, &_visualContextHAP);
    
    if (error != noErr)
    {
        NSLog(@"err %ld, couldnt create  Hap visual context at %s", error, __func__);
    }
    
    NSMutableDictionary* pixelBufferDict = [NSMutableDictionary dictionary];
    [pixelBufferDict setValue:[NSNumber numberWithBool:YES] forKey:(NSString*)kCVPixelBufferOpenGLCompatibilityKey];


     error = QTOpenGLTextureContextCreate(kCFAllocatorDefault,
                                                  _context,
                                                  CGLGetPixelFormat(_context),
                                                  (CFDictionaryRef)pixelBufferDict,
                                                  &_visualContextTexture);
  
    //
    // if this options are enabled we can get an error/warning logged on 10.9: CGCMSUtilsGetICCProfileDescription
    //
//    QTVisualContextSetAttribute(_visualContextTexture,kQTVisualContextWorkingColorSpaceKey, CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB));
//    QTVisualContextSetAttribute(_visualContextTexture,kQTVisualContextOutputColorSpaceKey, CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB));

    if (error != noErr)
    {
        NSLog(@"err %ld, couldnt create Texture visual context at %s", error, __func__);
    }
    
    
}


- (void) enableExecution:(id<QCPlugInContext>)context
{
	//	NSLog(@"enableExecution");
	/*
	 Called by Quartz Composer when the plug-in instance starts being used by Quartz Composer.
	 */
	
	self.outputMoviePlayerInited = NO;

	
	[self setNextSlotToTrigger:-1];
	
	
	
	
}

-(BOOL)canHandleFileAtURL:(NSURL *)_url {

	NSString *fileuti = nil;
	[_url getResourceValue:&fileuti forKey:NSURLTypeIdentifierKey error:nil];
	
	if ([fileuti isEqualToString:@"com.apple.quartz-composer-composition"]) {
		
		return NO;
	}
    
  //  [fileuti release];
	
	return YES;
}



-(void)stopActualMovie {

 //   NSDate *start = [NSDate date];

    int lastplayback = currentplayback;
    int lastbank = currentbank;
    
    if (currentmovie != nil) {
        
		if ([self inputNoResetOnTrigger]) {
			[self.movieData setObject:[NSValue valueWithQTTime:[currentmovie currentTime]] forKey:[NSString stringWithFormat:@"LastTime-%d-%d", lastbank, lastplayback]];
		} else {
			
			//This is important! If we miss this, we will see a frame jump on trigger (by unknown reason:/)!
			//[currentmovie gotoBeginning];
		}
        
        /* ... Do whatever you need to do ... */
        
		[currentmovie stop];
        SetMovieVisualContext([currentmovie quickTimeMovie], NULL);
        //[currentmovie setVisualContext:NULL];
        
        //not sure about calling autorelease is safe here
        //but seems it not producing leaking memory
        //and its much faster calling autorelease here then release
        //which really speeds up triggering speed
		[currentmovie autorelease];
        currentmovie = nil;
        
        
	}
    /*
    NSDate *stop = [NSDate date];
    NSTimeInterval duration = [start timeIntervalSinceDate:stop];
    
    NSLog(@"stop: %f", duration);
*/
}

- (BOOL)hasEnoughFreeMemory
{
    NSInteger memory = [self getMemoryUsedInBytes];
    
    memory=memory/1048576;
    
    NSInteger free = [self get_free_memory];
    
  //  NSLog(@"memory used: %ld", memory);
    
    return (memory<2500) && (free>300);
}

- (NSInteger)getMemoryUsedInBytes
{
    struct task_basic_info info;
    mach_msg_type_number_t size = sizeof(info);
    kern_return_t kerr = task_info(mach_task_self(),
                                   TASK_BASIC_INFO,
                                   (task_info_t)&info,
                                   &size);
    if( kerr == KERN_SUCCESS )
    {
        return info.resident_size;
    }
    else
    {
        return 0;
    }
}

-(natural_t) get_free_memory {
	mach_port_t host_port;
	mach_msg_type_number_t host_size;
	vm_size_t pagesize;
	host_port = mach_host_self();
	host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
	host_page_size(host_port, &pagesize);
	vm_statistics_data_t vm_stat;
	if (host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stat, &host_size) != KERN_SUCCESS) {
		NSLog(@"Failed to fetch vm statistics");
		return 0;
	}
    
    float temp = ((float)vm_stat.free_count*pagesize)/1024.0f/1024.0f;
    
	/* Stats in megabytes */
	natural_t mem_free = (temp);
    
	return mem_free;
}

-(void)triggerNewFileWithData:(NSArray *)data {
    
    NSNumber *newindex = [data objectAtIndex:0];
    NSNumber *bank = [data objectAtIndex:1];
    
    int lastplayback = currentplayback;
    int lastbank = currentbank;
    
    currentplayback = [newindex intValue];
    currentbank = [bank intValue];
    
    
    // if we have a movie...
    if (currentmovie) {
        
        // on the same slot and bank...
        if (lastplayback == currentplayback && lastbank == currentbank) {
            
    
            if ([self.moviePathes objectForKey:[NSString stringWithFormat:@"%d", [newindex intValue]]] != nil) {
                
                NSString *actual = [[[self.moviePathes objectForKey:[NSString stringWithFormat:@"%d", [newindex intValue]]] copy] autorelease];
                NSURL *newurl = [NSURL fileURLWithPath:[actual stringByStandardizingPath]];

            
                // the url is the same...
                if ([newurl isEqualTo:[currentmovie attributeForKey:QTMovieURLAttribute]]) {
                
                    //so, this case, we just reset the playback
                    
                    if (([self inputNoResetOnTrigger]) && ([self.movieData objectForKey:[NSString stringWithFormat:@"LastTime-%d-%d", [bank intValue], currentplayback]] != nil)) {
                        
                        [currentmovie setCurrentTime:[[self.movieData objectForKey:[NSString stringWithFormat:@"LastTime-%d-%d",[bank intValue], currentplayback]] QTTimeValue]];
                        
                    } else {
                        
                        [currentmovie gotoBeginning];
                    }
                    
                    [self setMovieLoopMode];
                    [self handleStartEndPoints];
                    
                    
                    return;
                    
                }
                

            }
                
        }
    }

    
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    
    
 //     NSDate *start = [NSDate date];

  //  NSLog(@"triggerNewFileAtIndex");
    
    //
    // Note to self: do NOT use old Quicktime accessors like SetMovieRate
    // since those producing random stop-playback issues when mixing calls with QTKit
    // especially on 10.9
    //
    
    movieended = NO;
    
    
    if ([preloadData objectForKey:[NSString stringWithFormat:@"%d-%d", [bank intValue], [newindex intValue]]]) {
    
        if (currentmovie) {
            

            if ([self isUsingPreloadedMedia]) {
 
                [currentmovie setRate:0.0f];
                SetMovieVisualContext([currentmovie quickTimeMovie], NULL);
               // [currentmovie setVisualContext:NULL];
                
                if ([self inputNoResetOnTrigger]) {
                    [self.movieData setObject:[NSValue valueWithQTTime:[currentmovie currentTime]] forKey:[NSString stringWithFormat:@"LastTime-%d-%d", lastbank, lastplayback]];
                } else {
                    
                    [currentmovie gotoBeginning];
                    
                }


            } else {
                
                if ([self inputNoResetOnTrigger]) {
                    [self.movieData setObject:[NSValue valueWithQTTime:[currentmovie currentTime]] forKey:[NSString stringWithFormat:@"LastTime-%d-%d", lastbank, lastplayback]];
                } else {
                    
                    [currentmovie gotoBeginning];
                    
                }

                [self stopActualMovie];
            }

            

        }

        currentmovie = nil;
        _visualContext = NULL;

     //   NSLog(@"init preloaded media...");
        
      //  NSDate *start = [NSDate date];

        currentmovie = [preloadData objectForKey:[NSString stringWithFormat:@"%d-%d", [bank intValue], [newindex intValue]]];

        

        [self setIsUsingPreloadedMedia:YES];
        
        
        [self setMovieLoopMode];
        [self handleStartEndPoints];
        [self setupAudioContext:[NSString stringWithString:self.inputAudioDeviceUID]];
        [self setVolume];
        [self setRateWithoutStop];
        
//        NSDate *stop = [NSDate date];
//        NSTimeInterval duration = [start timeIntervalSinceDate:stop];
//        NSLog(@"duration: %f", duration);
        
        if (([self inputNoResetOnTrigger]) && ([self.movieData objectForKey:[NSString stringWithFormat:@"LastTime-%d-%d", [bank intValue], currentplayback]] != nil)) {
            
            [currentmovie setCurrentTime:[[self.movieData objectForKey:[NSString stringWithFormat:@"LastTime-%d-%d",[bank intValue], currentplayback]] QTTimeValue]];
            
        }

        [self setVisualContextForCurrentMovie];
        
        movieFinished = NO;


    } else {


        [self setIsPlaybackHAPMovie:NO];

        if (currentmovie) {
            
            if ([self isUsingPreloadedMedia]) {
                
                if ([self inputNoResetOnTrigger]) {
                    [self.movieData setObject:[NSValue valueWithQTTime:[currentmovie currentTime]] forKey:[NSString stringWithFormat:@"LastTime-%d-%d", lastbank, lastplayback]];
                }

                [currentmovie stop];
                SetMovieVisualContext([currentmovie quickTimeMovie], NULL);
              //  [currentmovie setVisualContext:NULL];
                
                
            } else {
                
                [self stopActualMovie];
                
             //   NSLog(@"movie stopped");
            }
            
        }

        [self setIsUsingPreloadedMedia:NO];

        currentmovie = nil;
        _visualContext = NULL;

     //   NSLog(@"init NON preloaded media...");

        if ([self.moviePathes objectForKey:[NSString stringWithFormat:@"%d", [newindex intValue]]] != nil) {
            
            NSString *actual = [[self.moviePathes objectForKey:[NSString stringWithFormat:@"%d", [newindex intValue]]] copy];
            NSURL *newurl = [NSURL fileURLWithPath:[actual stringByStandardizingPath]];
            
            //    NSDate *start = [NSDate date];
            
            if (([QTMovie canInitWithFile:actual]) && ([self canHandleFileAtURL:newurl])) {
                
                
                
                NSMutableDictionary* movieAttributes = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                                                        newurl, QTMovieURLAttribute,
                                                        [NSNumber numberWithBool:NO],QTMovieLoopsAttribute,
                                                        [NSNumber numberWithBool:NO],QTMovieLoopsBackAndForthAttribute,
                                                        [NSNumber numberWithBool:YES], QTMovieOpenAsyncOKAttribute,
                                                        [NSNumber numberWithBool:NO], QTMovieOpenForPlaybackAttribute,
                                                        [NSNumber numberWithBool:YES], QTMovieEditableAttribute,
                                                        //[NSNumber numberWithBool:YES], QTMovieOpenAsyncRequiredAttribute,
                                                        nil];
                
                
                currentmovie = [[QTMovie alloc] initWithAttributes:movieAttributes error:NULL];
                
                [self setVisualContextForCurrentMovie];
                [self setMovieLoopMode];

                [self handleStartEndPoints];
                
                if (([self inputNoResetOnTrigger]) && ([self.movieData objectForKey:[NSString stringWithFormat:@"LastTime-%d-%d", [bank intValue], currentplayback]] != nil)) {
                    
                    [currentmovie setCurrentTime:[[self.movieData objectForKey:[NSString stringWithFormat:@"LastTime-%d-%d",[bank intValue], currentplayback]] QTTimeValue]];
                    
                } else {
                    
                    [currentmovie gotoBeginning];
                }
                
                //don't need to change the audio context on every movie change
                //it takes a lots of time
                //seems its enough to set up only once when its changed
                [self setupAudioContext:[NSString stringWithString:self.inputAudioDeviceUID]];
                [self setVolume];
                

                [self setRateWithoutStop];
                
               // NSLog(@"movie inited");
                
                
                movieFinished = NO;
                
            }
            
            [actual release];
        }
    }
    
    if (currentmovie) {
        
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(movieDidEnd:) name:QTMovieDidEndNotification object:currentmovie];

    }
	
    [self setMovieSize:NSZeroSize];
    
    [self setMovieChanged:YES];
	
 //   NSLog(@"triggered");
}

-(void)handleStartEndPoints {
    
//    NSLog(@"%f", self.inputEndPoint);
    
    if (currentmovie != nil) {
        
        if ((self.inputStartPoint != 0.0f) || (self.inputEndPoint != 1.0f)) {
            
            long movieScale = [[currentmovie attributeForKey:QTMovieTimeScaleAttribute] longValue]; /*get movie scale */

            
            QTTimeRange aRange;
            aRange.time = QTMakeTime([currentmovie duration].timeValue*self.inputStartPoint, movieScale); /* start at what they pick */
            aRange.duration = QTMakeTime(([currentmovie duration].timeValue*self.inputEndPoint)-([currentmovie duration].timeValue*self.inputStartPoint), movieScale); /* duration they pick */
            
            [currentmovie setSelection:aRange];
            
            [currentmovie setAttribute:[NSNumber numberWithBool:YES] forKey:QTMoviePlaysSelectionOnlyAttribute];

        
        } else {
            
            [currentmovie setAttribute:[NSNumber numberWithBool:NO] forKey:QTMoviePlaysSelectionOnlyAttribute];
            

        }

        
         
   //     [currentmovie play];

    }
    
    
}

-(void)stopCurrent {
	
	if ([self isCurrentMoviePlayable]) {
        [currentmovie setRate:0.0f];
	}
	
}

-(void)setRate {
 //   NSLog(@"setRate");
    //we don't need to stop here
//	[currentmovie stop];
    if (currentmovie) {
        [currentmovie setRate:[self workingRate]];
    }
	
}

-(void)setRateWithoutStop {
  //  NSLog(@"setRateWithoutStop");
    if (currentmovie) {
        [currentmovie setRate:[self workingRate]];
    }

}


-(void)setVolume {
	
    if (currentmovie) {
        [currentmovie setVolume:[self workingVolume]];
    }
	
}

-(void)setVisualContextForCurrentMovie {

        
 //  if (0)
    if (HapQTMovieHasHapTrackPlayable(currentmovie))
    {
        
     //   NSLog(@"HAP movie detected!");


        if (![self isPlaybackHAPMovie]) {

            [self setIsPlaybackHAPMovie:YES];

        }
        
        _visualContext = _visualContextHAP;

    
    } else {
    
        NSSize currentMovieSize;
        [[currentmovie attributeForKey:QTMovieNaturalSizeAttribute] getValue:&currentMovieSize];
        //    NSLog(@"size: %@", NSStringFromSize(currentMovieSize));
        
        
        NSMutableDictionary* pixelBufferDict = [NSMutableDictionary dictionary];
        [pixelBufferDict setValue:[NSNumber numberWithInt:currentMovieSize.width] forKey:(NSString*)kCVPixelBufferWidthKey];
        [pixelBufferDict setValue:[NSNumber numberWithInt:currentMovieSize.height] forKey:(NSString*)kCVPixelBufferHeightKey];
        //we should set this attribute again to fix Bug#617. Anyway, its weird...
        [pixelBufferDict setValue:[NSNumber numberWithBool:YES] forKey:(NSString*)kCVPixelBufferOpenGLCompatibilityKey];
        QTVisualContextSetAttribute(_visualContextTexture,kQTVisualContextPixelBufferAttributesKey, (CFDictionaryRef)pixelBufferDict);
    //    QTVisualContextSetAttribute(_visualContextTexture,kQTVisualContextWorkingColorSpaceKey, CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB));
    //    QTVisualContextSetAttribute(_visualContextTexture,kQTVisualContextOutputColorSpaceKey, CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB));



        if ([self isPlaybackHAPMovie]) {
            
            
            [self setIsPlaybackHAPMovie:NO];

        }

        _visualContext = _visualContextTexture;
        
    }
    

    //... and then reassociate so that movies which change their aperture or transport stream like the new size.
    SetMovieVisualContext([currentmovie quickTimeMovie], _visualContext);
   // [currentmovie setVisualContext:_visualContext];
    
}

-(void)getMovieSize {
	
	
	
	if (currentmovie != nil) {
		
		NSSize currentMovieSize;
        [[currentmovie attributeForKey:QTMovieNaturalSizeAttribute] getValue:&currentMovieSize];
        // from the v002 Movie Player - thanks Vade and Tom!
        // this is the MAGIC CODE that dings the QTVisualContext into outputting "square pixel" textures that QC is happy with.
 		[self setMovieSize:currentMovieSize];
        

	}
	
	
	self.movieOKToPlayBack =  ((!NSEqualSizes([self movieSize], NSZeroSize)) && (((currentmovie != nil) && ([[currentmovie attributeForKey:QTMovieLoadStateAttribute] longValue] >= QTMovieLoadStatePlayable))));
	
	//	NSLog(@"movieOKToPlayBack1: %d", self.movieOKToPlayBack);
	
}

-(BOOL)isCurrentMoviePlayable {
    
    if (![self movieChanged]) return YES;
	
	//	NSLog(@"%d - %i - %i", currentplayback, [[[self.movieData objectForKey:[NSString stringWithFormat:@"%d", currentplayback]] attributeForKey:QTMovieLoadStateAttribute] longValue], QTMovieLoadStatePlayable);
	
	//[self performSelectorOnMainThread:@selector(getMovieSize) withObject:nil waitUntilDone:YES];
	
	if (NSEqualSizes([self movieSize], NSZeroSize) || ([self movieChanged] == YES))  {
		[self getMovieSize];
        
		
		if ((!NSEqualSizes([self movieSize], NSZeroSize)) && (self.movieOKToPlayBack)) {
			[self setMovieChanged:NO];
           // NSLog(@"set NO for waitForTriggerNextSlot");
            waitForTriggerNextSlot = NO;

		}
	}
	
	//	NSLog(@"movieOKToPlayBack2: %d", self.movieOKToPlayBack);
    
    
	
	return self.movieOKToPlayBack;
	
}



-(void)setMoviePlayhead:(NSNumber *)newtime {
    
    [self setReadyToSetPlayhead:NO];
	
	//	NSLog(@"newtime: %f", [newtime floatValue]);
	
	QTTime duration = [currentmovie duration];
	NSNumber *scale = [currentmovie attributeForKey:QTMovieTimeScaleAttribute];
	
	QTTime currentTime = QTMakeTime([newtime floatValue] * duration.timeValue, [scale floatValue]);
	
	[currentmovie setCurrentTime:currentTime];
    
    [self handleStartEndPoints];

    [self setReadyToSetPlayhead:YES];

}


-(void)playCurrent {
	
	//setting the rate is better then perform play, because its handle actual playback speed and forward/backward playback as well
    [currentmovie setRate:[self workingRate]];
    [self handleStartEndPoints];
	movieFinished = NO;
}

-(void)pauseCurrent {
    
    [currentmovie setRate:0.0f];
}

-(BOOL)canPlayFileAtIndex:(int)index {

	if ([self.moviePathes objectForKey:[NSString stringWithFormat:@"%d", index]] != nil) {

		NSString *actual = [[[self.moviePathes objectForKey:[NSString stringWithFormat:@"%d", index]] copy] autorelease];
		NSURL *newurl = [NSURL fileURLWithPath:[actual stringByStandardizingPath]];
							
		return (([QTMovie canInitWithFile:actual]) && ([self canHandleFileAtURL:newurl]));
							
	} else {
		
		return NO;
	
	}
	
		
}


-(void)markTriggerNextSlot {
    	
	if (([self workingLoopMode] == 4) && (!waitForTriggerNextSlot))
	{
		
		int nextslot = currentplayback+1;
		
		if ([self.moviePathes count] > 0) {
			
			while (![self canPlayFileAtIndex:nextslot]) {
				//		NSLog(@"slot nil at index: %d", nextslot);
				nextslot++;
				if (nextslot>15) nextslot = 0;
				
				if ([self.moviePathes count] == 0) {
					nextslot = -1;
					break;
				}
			}
			
		} else {
			
			nextslot = -1;
			
		}
		
			NSLog(@"nextslot :%d", nextslot);
		if (nextslot != -1) {
			waitForTriggerNextSlot = YES;
		}
		
		[self setNextSlotToTrigger:nextslot];
	} else {
        
        NSLog(@"skip, because waitForTriggerNextSlot: %d", waitForTriggerNextSlot);
    }
	
}

-(void)movieDidEnd:(NSNotification *)note {
    
  //  NSLog(@"moviedidend!");
    
    movieended = YES;
        
    if ([self workingLoopMode] == 4) {
        
        [self performSelectorOnMainThread:@selector(markTriggerNextSlot) withObject:nil waitUntilDone:YES];

    }
}

-(void)setMovieLoopMode {
    
//    NSLog(@"movie loop mode will change to: %d", [self workingLoopMode]);
    
    switch ([self workingLoopMode]) {
            //simple loop
        case 0:
            //it seems we don't need this
            //using this stops playback with backward playback mode when movie start time reached
            [currentmovie setAttribute:[NSNumber numberWithBool:YES] forKey:QTMovieLoopsAttribute];
            [currentmovie setAttribute:[NSNumber numberWithBool:NO] forKey:QTMovieLoopsBackAndForthAttribute];
            break;
            //mirrored loop
        case 1:
            //	NSLog(@"mirrored loop mode, go backwards!");
            [currentmovie setAttribute:[NSNumber numberWithBool:YES] forKey:QTMovieLoopsAttribute];
            [currentmovie setAttribute:[NSNumber numberWithBool:YES] forKey:QTMovieLoopsBackAndForthAttribute];
            break;
            // no loop, hold last
        case 2:
            //we do nothing here
            [currentmovie setAttribute:[NSNumber numberWithBool:NO] forKey:QTMovieLoopsAttribute];
            [currentmovie setAttribute:[NSNumber numberWithBool:NO] forKey:QTMovieLoopsBackAndForthAttribute];
            
            break;
            // trigger next
        case 3:
            //no loop
            [currentmovie setAttribute:[NSNumber numberWithBool:NO] forKey:QTMovieLoopsAttribute];
            [currentmovie setAttribute:[NSNumber numberWithBool:NO] forKey:QTMovieLoopsBackAndForthAttribute];
            
            break;
            // trigger next
        case 4:
            //  NSLog(@"actualtime: %f", actualtime);
            //  NSLog(@"lasttime: %f", lasttime);
            //  NSLog(@"waitForTriggerNextSlot: %d", waitForTriggerNextSlot);
            [currentmovie setAttribute:[NSNumber numberWithBool:NO] forKey:QTMovieLoopsAttribute];
            [currentmovie setAttribute:[NSNumber numberWithBool:NO] forKey:QTMovieLoopsBackAndForthAttribute];
            break;
    }
    
    
}

-(void)stopPreloadedMovieWithKey:(NSString *)currentkey {

    if (currentmovie == [preloadData objectForKey:currentkey]) {
        
        [currentmovie stop];
        SetMovieVisualContext([currentmovie quickTimeMovie], NULL);
       // [currentmovie setVisualContext:NULL];
        
        currentmovie = nil;
        
        _visualContext = NULL;
        
        [self setIsPlaybackHAPMovie:NO];
        [self setIsUsingPreloadedMedia:NO];
    }
    
    [preloadData removeObjectForKey:currentkey];
    

}

-(void)processPreload {
    
    //
    // should use an autorelease pool here
    // since its a background thread?
    //
    //@autoreleasepool
    {
        
        [preloadQueue cancelAllOperations];
        
        NSDictionary *workdict = [NSDictionary dictionaryWithDictionary:[self.moviePathes objectForKey:@"mediaBankDataForPlayer"]];
        
        for (id currentkey in [workdict allKeys]) {
            
            id current = [workdict objectForKey:currentkey];
            
            if ([current isKindOfClass:[NSString class]]) {
                
                if ([current isEqualToString:@""]) {
                    
                    NSArray *bankslotdata = [currentkey componentsSeparatedByString:@"-"];
                    
                    [self performSelectorOnMainThread:@selector(stopPreloadedMovieWithKey:) withObject:currentkey waitUntilDone:YES];
                    
                    if ([[bankslotdata objectAtIndex:0] intValue] == currentbank && [[bankslotdata objectAtIndex:1] intValue] == currentplayback)
                    {
                        
                        [preloadstatechanged setObject:[NSNumber numberWithBool:NO] forKey:[NSString stringWithFormat:@"%d", [[bankslotdata objectAtIndex:1] intValue]]];
                    }
                    
                    
                    
                } else {
                    
                    QTMovie *m = [preloadData objectForKey:currentkey];
                    
                    if (!m || (m && ![[m attributeForKey:QTMovieURLAttribute] isEqualTo:[NSURL fileURLWithPath:[current stringByStandardizingPath]]])) {
                        
                        //  NSLog(@"start preload: %@", current);
                        
                        NSArray *bankslotdata = [currentkey componentsSeparatedByString:@"-"];
                        
                        [self performSelectorOnMainThread:@selector(stopPreloadedMovieWithKey:) withObject:currentkey waitUntilDone:YES];
                        
                        if ([[bankslotdata objectAtIndex:0] intValue] == currentbank && [[bankslotdata objectAtIndex:1] intValue] == currentplayback)
                        {
                            
                            [preloadstatechanged setObject:[NSNumber numberWithBool:NO] forKey:[NSString stringWithFormat:@"%d", [[bankslotdata objectAtIndex:1] intValue]]];
                        }
                        
                        if ([self hasEnoughFreeMemory]) {
                            
                            CoGeMoviePreload *preload = [[CoGeMoviePreload alloc] init];
                            
                            [preload setDelegate:self];
                            [preload setPath:current];
                            [preload setBank:[[bankslotdata objectAtIndex:0] intValue]];
                            [preload setSlot:[[bankslotdata objectAtIndex:1] intValue]];
                            
                            [preloadQueue addOperation:preload];
                            
                            [preload release];
                            
                        }
                        
                    }
                    
                }
                
            }
            
        }

    }
    
}

-(void)setPreloadedMovie:(QTMovie *)movie onBank:(int)bank onSlot:(int)slot {
    
    BOOL hadmemory = NO;
  //  NSLog(@"bank-slot: %d-%d", bank, slot);
    [preloadData removeObjectForKey:[NSString stringWithFormat:@"%d-%d", bank, slot]];
    
    if ([self hasEnoughFreeMemory]) {
        hadmemory = YES;
        [preloadData setObject:movie forKey:[NSString stringWithFormat:@"%d-%d", bank, slot]];
    }     
    if (bank == currentbank) {
        
        if (hadmemory) {
            
            [preloadstatechanged removeObjectForKey:[NSString stringWithFormat:@"%d", slot]];
            [preloadstatechanged setObject:[NSNumber numberWithBool:YES] forKey:[NSString stringWithFormat:@"%d", slot]];
        }
    }
    
}

-(void)stopAllMovies {
    
    if (currentmovie) {
        
        [currentmovie stop];
        SetMovieVisualContext([currentmovie quickTimeMovie], NULL);
      //  [currentmovie setVisualContext:NULL];
        
        currentmovie = nil;
        
        _visualContext = NULL;
        
        [self setIsPlaybackHAPMovie:NO];
        [self setIsUsingPreloadedMedia:NO];
        
        
    }
    
    for (QTMovie *current in [preloadData objectEnumerator]) {
        
        [current stop];
        SetMovieVisualContext([currentmovie quickTimeMovie], NULL);
        //[current setVisualContext:NULL];
        
    }
    
    [preloadData removeAllObjects];
}

- (BOOL) execute:(id<QCPlugInContext>)context atTime:(NSTimeInterval)time withArguments:(NSDictionary*)arguments
{
	/*
	 Called by Quartz Composer whenever the plug-in instance needs to execute.
	 Only read from the plug-in inputs and produce a result (by writing to the plug-in outputs or rendering to the destination OpenGL context) within that method and nowhere else.
	 Return NO in case of failure during the execution (this will prevent rendering of the current frame to complete).
	 
	 The OpenGL context for rendering can be accessed and defined for CGL macros using:
	 CGLContextObj cgl_ctx = [context CGLContextObj];
	 */	
    CGLContextObj cgl_ctx = [context CGLContextObj];
	
    if ([self didValueForInputKeyChange:@"inputPreloadMovies"]) {
        
        [preloadQueue cancelAllOperations];
        
        [self performSelectorOnMainThread:@selector(stopAllMovies) withObject:nil waitUntilDone:YES];

        if (self.inputPreloadMovies) {
        
            [self performSelectorInBackground:@selector(processPreload) withObject:nil];
            
        } else {
            
            for (int i=0;i<16;i++) {
                [preloadstatechanged setObject:[NSNumber numberWithBool:NO] forKey:[NSString stringWithFormat:@"%d", i]];
            }
        }
        
        needstrigger = YES;

    }
    
	if ([self didValueForInputKeyChange:@"inputCoGeLayerFilePathes"]) {
		
		//NSLog(@"inputCoGeLayerFilePathes");
 		
		if (self.inputCoGeLayerFilePathes != nil) 
		{
			
			BOOL iscurrentchanged = ![[self.moviePathes objectForKey:[NSString stringWithFormat:@"%d", currentplayback]] isEqualToString:[self.inputCoGeLayerFilePathes objectForKey:[NSString stringWithFormat:@"%d", currentplayback]]];
			self.moviePathes = [NSDictionary dictionaryWithDictionary:[[self.inputCoGeLayerFilePathes copy] autorelease]];
			
            int newbank = [[[self.moviePathes objectForKey:@"mediaBankDataForPlayer"] objectForKey:@"selectedBank"] intValue];
            
            if (newbank != currentbank) {
                
                needstrigger = YES;
                
                [preloadstatechanged removeAllObjects];
                
                for (int i=0;i<16;i++) {
                    
                    if ([preloadData objectForKey:[NSString stringWithFormat:@"%d-%d", newbank, i]]) {
                        
                        [preloadstatechanged setObject:[NSNumber numberWithBool:YES] forKey:[NSString stringWithFormat:@"%d", i]];
                    
                    } else {
                        
                        [preloadstatechanged setObject:[NSNumber numberWithBool:NO] forKey:[NSString stringWithFormat:@"%d", i]];
                    
                    }
                    
                }
            }
            
            currentbank = newbank;
            
            if (self.inputPreloadMovies) {
                
             //   NSLog(@"process preload...");
                
                [self performSelectorInBackground:@selector(processPreload) withObject:nil];
                
            }
            
			if (self.inputBankChanged == YES) {
                
				shouldProcessBecauseMediaBankChanged = YES;
								
				needstrigger = YES;
				
			} else {
			
				if (((![self didValueForInputKeyChange:@"inputSlotClicked"]) || ([self.moviePathes count] == 0)))  {
					
					if (iscurrentchanged) {
						
						needstrigger = YES;
					}
					
				} else {
					
					//NSLog(@"skipped rebuild!");
				}
				
			}
			
			
			
		}
		
		
		
	}
		
	if ((((([self didValueForInputKeyChange:@"inputSlotClicked"]) && (self.inputSlotClicked))) || (needstrigger))) {
		
		if ([self isCurrentMoviePlayable]) {
			
			needstrigger = NO;
			
		}
		
		//NSLog(@"inputMovieIndex : %d", self.inputMovieIndex);
		
		//
		// Seems we must cache our value, because inside the selector self.movieIndex always returns 0 if we not wait until done?
		//
		int x = self.inputMovieIndex;
		
		
        //we need to waitUntilDone on these commands!
		//NSLog(@"currentplayback: %d", currentplayback);
      //  NSDate *start = [NSDate date];
      //  [self performSelectorOnMainThread:@selector(stopActualMovie) withObject:nil waitUntilDone:YES];
        
      //  [self performSelectorOnMainThread:@selector(triggerNewFileWithData:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithInt:x], [NSNumber numberWithInt:currentbank], nil] waitUntilDone:YES];
        
      //  NSLog(@"trigger at: %f", [NSDate timeIntervalSinceReferenceDate]*1000);
        dispatch_sync(dispatch_get_main_queue(), ^{
            
            [self triggerNewFileWithData:[NSArray arrayWithObjects:[NSNumber numberWithInt:x], [NSNumber numberWithInt:currentbank], nil]];
            
        });
        
        
    //    NSDate *stop = [NSDate date];
    //    NSTimeInterval duration = [start timeIntervalSinceDate:stop];
        
    //    NSLog(@"duration: %f", duration);

        [self setNextSlotToTrigger:-1];
        
        

	}
    
    
    
	if ([self didValueForInputKeyChange:@"inputRate"]) {
		
		[self setWorkingRate:self.inputRate];
        		
		[self performSelectorOnMainThread:@selector(setRate) withObject:nil waitUntilDone:NO];
		
	//	NSLog(@"rate changed!");
		
		
	}
	
	if ([self didValueForInputKeyChange:@"inputVolume"]) {
		
		[self setWorkingVolume:self.inputVolume];
		[self performSelectorOnMainThread:@selector(setVolume) withObject:nil waitUntilDone:NO];
		
		
	}
	
	if ([self didValueForInputKeyChange:@"inputIsActive"]) {
		
		if (self.inputIsActive) {
			
			shouldProcessBecauseMediaBankChanged = YES;

            if ([self isCurrentMoviePlayable]) {
                                
                if ((self.inputBankChanged) || (currentplayback != self.inputMovieIndex)) {
                    
                    int x = self.inputMovieIndex;
                    
                    [self performSelectorOnMainThread:@selector(triggerNewFileWithData:) withObject:[NSArray arrayWithObjects:[NSNumber numberWithInt:x], [NSNumber numberWithInt:currentbank], nil] waitUntilDone:YES];
                    
                    
                    [self setNextSlotToTrigger:-1];
                    
                } else {

                    [self performSelectorOnMainThread:@selector(setRateWithoutStop) withObject:nil waitUntilDone:NO];

                }
                
            } else {

                needstrigger = YES;

            }
			
		} else {
            
            if ([self inputNoResetOnTrigger]) {
                [self.movieData setObject:[NSValue valueWithQTTime:[currentmovie currentTime]] forKey:[NSString stringWithFormat:@"LastTime-%d-%d", currentbank, currentplayback]];
            }
			
			[self performSelectorOnMainThread:@selector(stopCurrent) withObject:nil waitUntilDone:NO];
			
			
		}
	}
	
	if ([self didValueForInputKeyChange:@"inputStartPoint"]) {
		
        // this should be synchonious or sometimes it does not catch the change
		[self performSelectorOnMainThread:@selector(handleStartEndPoints) withObject:nil waitUntilDone:YES];
		
	}
	
	if ([self didValueForInputKeyChange:@"inputEndPoint"]) {
		
        // this should be synchonious or sometimes it does not catch the change
		[self performSelectorOnMainThread:@selector(handleStartEndPoints) withObject:nil waitUntilDone:YES];
		
	}
	
	if ([self didValueForInputKeyChange:@"inputLoopMode"]) {
		
		[self setWorkingLoopMode:self.inputLoopMode];
        
		[self performSelectorOnMainThread:@selector(setMovieLoopMode) withObject:nil waitUntilDone:NO];
		[self performSelectorOnMainThread:@selector(setRateWithoutStop) withObject:nil waitUntilDone:NO];
		
	}
	
	
	if (([self didValueForInputKeyChange:@"inputAudioDeviceUID"]) && (![self.inputAudioDeviceUID isEqualToString:@""])) {
		
		NSLog(@"audio context changed!");
		
		[self performSelectorOnMainThread:@selector(setupAudioContext:) withObject:[NSString stringWithString:self.inputAudioDeviceUID] waitUntilDone:NO];
        
        [self performSelectorOnMainThread:@selector(setRateWithoutStop) withObject:nil waitUntilDone:NO];

	}
	
	
#pragma mark Rendering	
    
    

    
	if (([self isCurrentMoviePlayable]) && (self.inputIsActive)) {
		
     //   NSLog(@"here, movie is %d", currentmovie == nil);
        
     //   NSLog(@"palindrome: %d", [[currentmovie attributeForKey:QTMovieLoopsBackAndForthAttribute] boolValue]);
        
		// that's the best way tell CoGe we are ready, the plugin activated
		self.outputMoviePlayerInited = YES;
		
		movieFinished = NO;
		
		
		if ([self didValueForInputKeyChange:@"inputPlayhead"]) {
            
						
			if (self.inputPlayhead <= -1.0f) {
				
				if ([self workingRate] != 0.0f) {
					//NSLog(@"here");
					
					[self performSelectorOnMainThread:@selector(playCurrent) withObject:nil waitUntilDone:NO];
					
				}
                   
                    
				
			} else {
                
                
                if (![self shouldStartMovie]) {
 
                //    NSLog(@"stop");

                    [self performSelectorOnMainThread:@selector(pauseCurrent) withObject:nil waitUntilDone:NO];
                    [self setShouldStartMovie:YES];
                    
                }
                
                if ([self readyToSetPlayhead]) {
                    [self performSelectorOnMainThread:@selector(setMoviePlayhead:) withObject:[NSNumber numberWithDouble:self.inputPlayhead] waitUntilDone:NO];
                }
				
 
			}
            

			
		} else {
            
            if (([self shouldStartMovie]) && ([self readyToSetPlayhead])) {
                
             //   NSLog(@"start");
                
                [self performSelectorOnMainThread:@selector(setRateWithoutStop) withObject:nil waitUntilDone:NO];
                [self setShouldStartMovie:NO];
                
            }
        }
         
        /*
        dispatch_sync(dispatch_get_main_queue(), ^{
            long movieTimeScale = [currentmovie currentTime].timeScale;
            [currentmovie setCurrentTime:QTMakeTime((long long)(time * (double)movieTimeScale), movieTimeScale)];
        });
		*/
        
        if (provider) {
            [provider release];
            provider = NULL;
        }

		[self performSelector:@selector(grabActualFrame)];
        
        
		if ((_latestTextureFrame != NULL) && (!skipNextFrame))
		{
            
           
            if ([self isPlaybackHAPMovie]) {
                
             //   NSLog(@"isPlaybackHAPMovie");
                
            
                if (hapTexture == nil)
                {
                    hapTexture = [[HapPixelBufferTexture alloc] initWithContext:[context CGLContextObj]];
                    

                }
                
                hapTexture.buffer = _latestTextureFrame;
                
                NSSize imageSize = NSMakeSize(hapTexture.width, hapTexture.height);
                NSSize textureSize = NSMakeSize(hapTexture.textureWidth, hapTexture.textureHeight);
                
             //   NSLog(@"textureSize: %@", NSStringFromSize(textureSize));
                                
                provider = [[CoGeMovieImageProvider alloc] initWithTexture:hapTexture.textureName target:GL_TEXTURE_2D imageSize:imageSize textureSize:textureSize flipped:YES shader:hapTexture.shaderProgramObject];

                
            } else {
                
                
                provider = [[CoGeMovieImageProvider alloc] initWithTexture:CVOpenGLTextureGetName(_latestTextureFrame) target:CVOpenGLTextureGetTarget(_latestTextureFrame) imageSize:[self movieSize] textureSize:[self movieSize] flipped:CVOpenGLTextureIsFlipped(_latestTextureFrame) shader:NULL];
                
 
            }

			
			
			
			
		} else {
            
         //   NSLog(@"here");
            
            if (skipNextFrame) {
            
                NSLog(@"skipping frame!");
                skipNextFrame = NO;
                
            } else {
            
                provider = NULL;

            }
            
        }
		
		
		double actualtime = [self movieNormalizedTime];
		self.outputMovieTime = actualtime;
		
		
	}  	else {
		
		//	NSLog(@"movie is not playable, outputs nil!");
		
        if (![self isCurrentMoviePlayable]) {
            
         //   NSLog(@"not isCurrentMoviePlayable");
        }
		
		provider = NULL;
		self.outputMovieTime = 0.0f;
		movieFinished = NO;
		
	}
    
    if ((movieended) && ([self workingLoopMode] == 3)) {
        
        provider = NULL;
    }
         
         
	
#pragma mark Outputs	
	
 //   NSLog(@"CoGeMoviePlayer outputing: %@", [provider className]);
	
	self.outputImage = provider;
	self.outputMovieReachedEndPoint = movieFinished;
    self.outputSlotMarkedToTrigger = [self nextSlotToTrigger];
    if (preloadstatechanged && [preloadstatechanged count]>0) {
        self.outputPreloadStateChanges = [NSDictionary dictionaryWithDictionary:[[preloadstatechanged copy] autorelease]];
        [preloadstatechanged removeAllObjects];
    } else {
        self.outputPreloadStateChanges = nil;
    }
    
    if (_visualContext != NULL) {
        
        QTVisualContextTask(_visualContext);

    }
	   
	return YES;
}


//this one from V002 MoviePlayer
- (void)grabActualFrame
{
    
	if (_visualContext != NULL) {
    
        
        //_without QTVisualContextIsNewImageAvailable call
        //we got more stable FPS and less CPU usage!_
        //NO!!!
        //this call should be enabled for better performance, related to CoGe Bug#442
		if (QTVisualContextIsNewImageAvailable(_visualContext, NULL))
		{
		//	NSLog(@"new image!");
			
			if (_latestTextureFrame != NULL)
			{	
				CVBufferRelease(_latestTextureFrame);
				_latestTextureFrame = NULL;
			}
            
            CVReturn ret = QTVisualContextCopyImageForTime(_visualContext, NULL, NULL, &_latestTextureFrame);
			
			if (ret != kCVReturnSuccess) {
				NSLog(@"problem with QTVisualContextCopyImageForTime! %d", ret);
			} else {
             

             //   if (_latestTextureFrame != NULL) NSLog(@"OK!");

            }
		}
        
        


		
	}
	

}
//
//from v002 Movie Player by vade and bangnoise
//
- (BOOL)setupAudioContext:(NSString*)deviceUID
{
	// Lifted from Technical Q&A QA1578 by toby*spark
	
	// Pass in a fully initialized QTKit QTMovie object and an Audio Device UID CFStringRef
	// NOTE: The Audio Device UID String is the persistent kAudioDevicePropertyDeviceUID property returned
	//       using AudioDeviceGetProperty.
	
//    NSLog(@"needed: %@    def: %@", deviceUID, defAudioDevice);
    
    if ([deviceUID isEqualToString:defAudioDevice]) {
        
  //      NSLog(@"default audio device!");
        
        return NO;
    }
    
	Movie aMovie;
	QTAudioContextRef audioContext = NULL;
	OSStatus status = paramErr;
	
	
	//safety check
	if (currentmovie == nil) return NO;
         
	
	
	aMovie = [currentmovie quickTimeMovie];
    
    if (GetMovieAudioContext(aMovie, &audioContext) != noErr) {
        
     //   NSLog(@"no audio channel!");
        
        return NO;
    }
    
    //we skip here if the movie got no audio
    if (audioContext == NULL)  {
        
        return NO;
    }
    
    [currentmovie stop];


	if (NULL == aMovie) return status;
	
	// create a QT Audio Context and set it on a Movie
	status = QTAudioContextCreateForAudioDevice(kCFAllocatorDefault, ([deviceUID length] == 0 ? NULL : (CFStringRef)deviceUID), NULL, &audioContext);
	if (status) NSLog(@"QTAudioContextCreateForAudioDevice failed: %d\n", (int)status);
	
	if (NULL != audioContext && noErr == status) 
	{
		status = SetMovieAudioContext(aMovie, audioContext);
		if (status) NSLog(@"SetMovieAudioContext failed: %d\n", (int)status);
		
		// release the Audio Context since SetMovieAudioContext will retain it
		CFRelease(audioContext);
	} else {
		
	//	NSLog(@"audio context set up succesfully!");
	}
	
	
	return status;
}


-(double)movieNormalizedTime
{
	
    if (currentmovie != nil) {
		
        QTTime duration = [currentmovie duration];
        QTTime currentTime = [currentmovie currentTime];	
        return ((float)currentTime.timeValue/(float)duration.timeValue);	
		
    } else {
        
        return 0.0;
    }
	
}


- (void) disableExecution:(id<QCPlugInContext>)context
{
	/*
	 Called by Quartz Composer when the plug-in instance stops being used by Quartz Composer.
	 */
	
	if ([self isCurrentMoviePlayable]) {
		[self performSelectorOnMainThread:@selector(stopCurrent) withObject:nil waitUntilDone:YES];
	}
	
		NSLog(@"disableExecution");
	
}

-(void)cleanUpOnStop {
    
    
    [preloadQueue cancelAllOperations];
    [preloadQueue release];
    preloadQueue = nil;
    
    [[NSNotificationCenter defaultCenter] removeObserver:self];
	
    [self stopActualMovie];
    
    if (preloadstatechanged) {
        
        [preloadstatechanged release];
        preloadstatechanged = nil;
    }
    
    
	if (_latestTextureFrame) {
		CVBufferRelease(_latestTextureFrame);
        _latestTextureFrame = NULL;
	}
	
	if (_visualContext != NULL) {
		_visualContext = NULL;
	}
	
	if (_visualContextHAP != NULL) {
        QTVisualContextRelease(_visualContextHAP);
		_visualContextHAP = NULL;
	}

    if (_visualContextTexture != NULL) {
        QTVisualContextRelease(_visualContextTexture);
		_visualContextTexture = NULL;
	}
    
    if (hapTexture != nil) {
        
        [hapTexture release];
        hapTexture = nil;
    }


	if (movieData != nil) {
		[movieData release];
		movieData = nil;
		
	}
    
    CGLReleaseContext(_context);
	
    NSLog(@"cleanup done!");
}

- (void) stopExecution:(id<QCPlugInContext>)context
{
	/*
	 Called by Quartz Composer when rendering of the composition stops: perform any required cleanup for the plug-in.
	 */
	
	NSLog(@"stopExecution on CoGeMovie");
	
//	[[NSNotificationCenter defaultCenter] removeObserver:self];

	
	
	[self performSelectorOnMainThread:@selector(cleanUpOnStop) withObject:nil waitUntilDone:YES];
	
	
}

@end

@implementation CoGeMovieImageProvider
-(id)initWithTexture:(GLuint)_tex target:(GLenum)_target imageSize:(NSSize)_size textureSize:(NSSize)_texsize flipped:(BOOL)flip shader:(GLhandleARB)_shader
{
	if(self = [super init])
	{
		imageBounds = NSMakeRect(0, 0, _size.width, _size.height);
		
		target = _target;
		textureName = _tex;
        textureSize = _texsize;
        imageSize = _size;
        isFlipped = flip;
        shader = _shader;
        
        _colorspace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
	}
	return self;
}

// required for protocol compliance
- (NSRect) imageBounds
{
	return imageBounds;
}

- (BOOL) shouldColorMatch
{
	return YES;
}

-(void)finalize {
    
    CGColorSpaceRelease(_colorspace);
    
    [super finalize];
}

-(void)dealloc {
    
   // if (textureName != 0) glDeleteTextures(1, &textureName);
    
    CGColorSpaceRelease(_colorspace);
    
    [super dealloc];
}

- (CGColorSpaceRef) imageColorSpace
{
	return _colorspace;
}
/*- (NSArray*) supportedRenderedTexturePixelFormats
 {
 #if __BIG_ENDIAN__
 return [NSArray arrayWithObject:QCPlugInPixelFormatARGB8];
 #else
 return [NSArray arrayWithObject:QCPlugInPixelFormatBGRA8];
 #endif
 }
 */

// required for using OpenGL providers
- (BOOL) canRenderWithCGLContext:(CGLContextObj)cgl_ctx
{
	return YES;
}


-(void)releaseRenderedTexture:(GLuint)name forCGLContext:(CGLContextObj)cgl_ctx
{
  //  glDeleteTextures(1, &name);
}

- (BOOL) renderWithCGLContext:(CGLContextObj)cgl_ctx forBounds:(NSRect)bounds
{
    

	glPushAttrib(GL_ENABLE_BIT | GL_TEXTURE_BIT | GL_TRANSFORM_BIT);
	glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);

    //are this disables really needed?
   // glDisable(GL_DEPTH_TEST);
    glDisable(GL_BLEND);
    
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glPushMatrix();
    
    //is this really needed?
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glPushMatrix();
    
    glViewport(0, 0, (GLsizei) bounds.size.width, (GLsizei) bounds.size.height);
    glOrtho(bounds.origin.x, bounds.origin.x+bounds.size.width, bounds.origin.y, bounds.origin.y+bounds.size.height, -1.0, 1.0);
    
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
    
    glClearColor(0.0,0.0,0.0,0.0);
    glClear(GL_COLOR_BUFFER_BIT);
    glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
    
    glEnable(target);
    
    NSRect destRect = NSMakeRect(0,0,0,0);
    double bAspect = bounds.size.width/bounds.size.height;
    double aAspect = imageSize.width/imageSize.height;
    
    // if the rect i'm trying to fit stuff *into* is wider than the rect i'm resizing
    if (bAspect > aAspect)
    {
        destRect.size.height = bounds.size.height;
        destRect.size.width = destRect.size.height * aAspect;
    }
    // else if the rect i'm resizing is wider than the rect it's going into
    else if (bAspect < aAspect)
    {
        destRect.size.width = bounds.size.width;
        destRect.size.height = destRect.size.width / aAspect;
    }
    else
    {
        destRect.size.width = bounds.size.width;
        destRect.size.height = bounds.size.height;
    }
    destRect.origin.x = (bounds.size.width-destRect.size.width)/2.0+bounds.origin.x;
    destRect.origin.y = (bounds.size.height-destRect.size.height)/2.0+bounds.origin.y;
    
    GLfloat vertices[] =
    {
        destRect.origin.x,                          destRect.origin.y,
        destRect.origin.x+destRect.size.width,      destRect.origin.y,
        destRect.origin.x + destRect.size.width,    destRect.origin.y + destRect.size.height,
        destRect.origin.x,                          destRect.origin.y + destRect.size.height,
    };
    
    GLfloat texCoords[] =
    {
        0.0,        (isFlipped ? imageSize.height : 0.0),
        imageSize.width,   (isFlipped ? imageSize.height : 0.0),
        imageSize.width,   (isFlipped ? 0.0 : imageSize.height),
        0.0,        (isFlipped ? 0.0 : imageSize.height)
    };
    
    if (target == GL_TEXTURE_2D)
    {
        texCoords[1] /= (float)textureSize.height;
        texCoords[3] /= (float)textureSize.height;
        texCoords[5] /= (float)textureSize.height;
        texCoords[7] /= (float)textureSize.height;
        texCoords[2] /= (float)textureSize.width;
        texCoords[4] /= (float)textureSize.width;
    }
    
    glBindTexture(target,textureName);
    
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    glVertexPointer(2,GL_FLOAT,0,vertices);
    glTexCoordPointer(2,GL_FLOAT,0,texCoords);

    
    if (shader != NULL)
    {
        glUseProgramObjectARB(shader);
    }
    glDrawArrays(GL_QUADS,0,4);
    glDisableClientState( GL_TEXTURE_COORD_ARRAY );
    glDisableClientState(GL_VERTEX_ARRAY);

    
    if (shader != NULL)
    {
        glUseProgramObjectARB(NULL);
    }
    
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
    glPopMatrix();
    glPopClientAttrib();
    glPopAttrib();

	
	return YES;
}

@end

